Delphi 4 Review

Delphi 4

Reviewed by Brian Long

DelphiSplash.GIF

This review was published in the August 1998 issue (No 5) of Developers Review. It was also included on the companion disk with the August 1998 issue (No 36) of The Delphi Magazine. This HTML version of the review may be freely distributed.

Developers Review

Any developer worth his or her salt will agree that thereÆs no point reinventing the wheel. When creating world-beating applications to tight deadlines itÆs essential to have the right tools to hand. ThereÆs only one problem: how can you ensure you choose the best tools for the job? Developers Review changes all that. Whilst there are a number of magazines around for software developers, Developers Review is unique in focussing entirely on reviews, surveys and news of desktop software development products. The magazine is the source of information for developers who need to know "What tool should I choose to do...". For more information on how to subscribe and tons of free sample material, visit www.itecuk.com/devrev.

The Delphi Magazine

Published monthly, each issue of The Delphi Magazine is packed with ideas to help you get the most out of Delphi: the development tool from Inprise which has taken the world by storm! We cater especially for professional Delphi developers and our team of authors includes some of the foremost Delphi experts in the world. For more information on how to subscribe and tons of free sample material, visit www.itecuk.com/delmag.

Developers Review and The Delphi Magazine are published by:
iTec, 9a London Road, BROMLEY, Kent BR1 1BY, United Kingdom.
Telephone: +44 (0)181 249 0354, Fax: +44 (0)181 249 0376, Email: mail@itecuk.com

Another version of Delphi? Absolutely! A little more than a year after version 3 was released we now have Borland Delphi 4 from Inprise with plenty of bells, whistles and other musical accompaniments. In this review we will trawl through all that is new in the package and try to identify if it is worth smartly running to the shops to buy it.

Of course all this new functionality supplied in Delphi 4 will potentially upset purchasers of the recently released Borland C++Builder 3 (BCB 3) who, again, now seem to be lagging behind in product features. But, given time, BCB 4 will have all this stuff and more besides, I am sure. The Delphi and C++Builder teams seem to be repeatedly leapfrogging each other with each new release.

Incidentally, if you have read the review of Borland C++Builder 3 in Issue 3 of Developers Review then a certain amount of this information will sound familiar. But there is much that is new to be found in Delphi 4. For detailed information on Delphi 3, check out the review in Issue 1 of this periodical: this review will be primarily focusing on

The Modern IDE

First of all, letÆs take a look at Delphi 4Æs overhauled IDE. Figure 1 shows how it might look in a given development session. I use the word æmightÆ since it is now very configurable, thanks to the presence of floating toolbars and dockable tool windows. If you want the various debugger support windows, such as the breakpoint list, call stack and watch window onscreen all the time, but without causing too much clutter, you can dock them all into one tabbed window, as shown in Figure 1.

DelphiIde.GIF
Figure 1

DelphiIde2.GIF
Figure 2

The IDE still uses the SDI (Single Document Interface) approach, but if your desires wander off towards an environment where you donÆt have countless windows then you could dock practically all of them, other than the main window, together into one big multipurpose window. Figure 2 shows the same environment as Figure 1, but with all the windows except the alignment palette and the form designer merged together. This is not quite MDI, but it gives you more options.

Delphi 4 sports a much enhanced project manager. The project manager from Delphi 3 allowed you to navigate around the current project. The new project manager supports a whole group of projects at once. Only one project is active at any time, but you can store related projects in a project group file. This means that you can manage an application and several DLLs in the same project group. Or maybe an application and several packages, or even some client applications and a middle tier application server written using COM and/or CORBA (more of which later).

A project group file has a .BPG extension and is just a simple makefile managed by the IDE. Incidentally, talking (almost) of command line support, whenever a project is saved in the IDE, a command line compiler configuration file (.CFG) is now saved. This means that if you run MAKE against the BPG file, all the projects will be compiled with DCC32.EXE and will get the same compiler/linker settings as you set in the design-time environment.

The File|New... dialog has a larger population of choices. You can now make an NT service application project (File|New...| Service Application), with individual NT services represented by TService objects (File|New...|Service). Also, you can create and manage a batch file project: a source-less mechanism to enable you to run batch files from the IDE.

Additionally, IDE support for localised resource DLLs is now available through a Resource DLL Wizard. This examines a compiled project and generates all the files necessary for a resource DLL for a specified locale. The forms in that DLL can be edited in the IDE in isolation from any source code, and the resource strings can be translated using a new string table editor.

Delphi 3 introduced these resource DLLs and the idea is that, as an application starts, it loads up the appropriate DLL based upon the current country/ locale setting (or registry-based overrides). The extended RICHEDIT demo supplied with Delphi 4 shows how you can dynamically switch between resource modules at runtime as well.

Another international development enhancement can be accessed through the Edit | Flip Children submenu (which surfaces a new FlipChildren method of TWinControl). Some countries read from right to left instead of left to right. FlipChildren changes the left bias of the entire form, flipping all controls within the selected container about the centre line of the form. Controls that are laid out left justified with ragged right edges are flipped around to be right justified, with ragged left edges. This is primarily useful for translating a Delphi application to Hebrew or Arabic and can provide translators a large time saving when localising forms.

Apart from the appearance of a Wizard to help create a web server application based around the contents of a given dataset (found on the Business page), the remaining additions to the File|New... dialog seem to be to do with the general topic of distributed applications (see later).

Delphi 3 Client/ServerÆs Remote Data Module (for MIDAS applications) has moved to the Multitier page and now sits alongside options for generating a CORBA object and a CORBA remote data module as well as an MTS object and an MTS data module. These will be covered later in the review.

AppBrowser IDE

Most of the attention on the IDE seems to be focused on the productivity enhancements directed specifically at coding and development. The environment is now described as having an AppBrowser IDE, which means there are features available that make it easier to navigate around the source code in a given project.

The most obvious feature is the Code Explorer, which by default is docked with the Code Editor, sitting to its left (see Figures 1 and 2). This is a tree view of practically everything in the currently active source file. You can see all the classes, interfaces, variables/constants, procedures, used units and other types on various nodes of the tree. Where appropriate, you can drill down to a certain extent, for example with classes you can examine the various sections (eg published and public) and with interfaces you can examine the properties and methods. Double-clicking in the Code Explorer takes you to the relevant place in the source code, and Shift+Ctrl+E toggles between the editor and the Code Explorer. The Code Explorer can be customised by the options on the dedicated page in Tools | Environment Options... | Explorer.

Another, less visible, productivity enhancer is class completion. This helps automate the task of manufacturing class declarations and implementations. At its simplest, you can type a method declaration in a class, then right-click and choose Complete class at cursor, or press Shift+Ctrl+C. This will add an appropriate method implementation in the unit implementation section. Likewise, you can type in an implementation and Shift+ Ctrl+C will insert the declaration. Rather more interestingly, it will also finish off incomplete property declarations. An example will help clarify what it can do. If you have some code in a unit that looks like Listing 1, clicking on it and pressing Shift+Ctrl+C will leave it looking like Listing 2, ready for you to fill in the blanks. Notice that the property gets a private underlying data field defined, and also a property routine declared and implemented.


Listing 1

unit Unit2;
interface
uses Classes;
type
  TMyComponent = class(TComponent)
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Special: Boolean;
  end;
implementation
procedure TMyComponent.LookupValue;
begin
end;
end.


Listing 2

unit Unit2;
interface
uses
  Classes;
type
  TMyComponent = class(TComponent)
  private
    FSpecial: Boolean;
    procedure LookupValue;
    procedure SetSpecial(const Value: Boolean);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Special: Boolean read FSpecial write SetSpecial;
  end;
implementation
constructor TMyComponent.Create(AOwner: TComponent);
begin
end;
destructor TMyComponent.Destroy;
begin
end;
procedure TMyComponent.LookupValue;
begin
end;
procedure TMyComponent.SetSpecial(const Value: Boolean);
begin
  FSpecial := Value;
end;
end.

More keystrokes allow you to perform navigation operations. Both Shift+Ctrl+¡ and Shift+Ctrl+» will jump from the declaration of a method to its implementation and back again. They also jump between the declaration of a subroutine and its implementation. The declaration can be in the interface section of a unit, or it can be a forward declaration in the implementation section. The term Module Navigation is used to describe these keystrokes.

The module navigation facilities are not over yet, not by a long chalk. For anyone who has bemoaned the lack of a ægo to this symbolÆs declarationÆ option for years, your cries have been answered. Functionality called the Code Browser allows you to do just this. To start with, you can easily find out where a given symbol was defined. Just pause your mouse over the symbol in the code editor at design-time, and the so-called Tooltip Symbol Insight tells you the exact location. Figure 1 shows Tooltip Symbol Insight giving information about TCustomForm.ShowModal. In order to be taken to the specified source file and location you can have a choice of two options. You can press the Ctrl key, whereby the symbol under the mouse cursor turns into a hyperlink. Clicking will then take you to the declaration. Alternatively, you can right-click and choose Find declaration.

In addition to these major functionality enhancements, there are many little things that make the environment a much nicer place to work in. These include a tooltip on the form designer to tell you the name and type of component your mouse is paused over, or the coordinates that your mouse is at when moving or resizing a component (Tools | Environment Options... | Preferences | Show designer hints). A tooltip also shows up in the Object Inspector when the entirety of a property name or value under the mouse is not visible. This is consistent with the way Windows Explorer and other applications operate.

Since the VCL now supports the Microsoft IntelliMouse wheel, the Delphi editor reacts to it as you would expect, scrolling up and down in tandem with the wheel. In your programs, TForms, TRichEdits, TStringGrids and TDrawGrids also surface events that allow you to do likewise.

The IDE is now chock-full of new menu shortcut keystrokes. For example, Ctrl+F11 (File | Open Project...), Shift+F11 (Project | Add to Project...), Shift+Ctrl+F11 (Project | Options...), Alt+F11 (File | Use Unit...), Alt+G (Search | Go to Line Number...). IÆve noticed a certain F11 theme to most of these new key combinations.

The editorÆs context menu now includes the bookmark options again, after a prolonged absence (they havenÆt been seen on a menu since Delphi 1).

A final point that I am thankful for is that when you double-click a Delphi file in Windows Explorer, a second instance of Delphi is no longer invoked. Instead, the file is opened in the currently running instance.

Language Enhancements

Now we can check out the additions to the Object Pascal language in version 12 of BorlandÆs Pascal compiler. Delphi 4 expands the Object Pascal language with dynamic arrays, method overloading, default parameters and 64-bit integers, plus more besides.

A dynamic array is a new construct that sits somewhere between normal static arrays, where you have as many items as you declare, and TLists, where you have a dynamic data structure with several supporting routines. A dynamic array allows you to declare an array structure with no pre-conceived idea as to how big it is. Admittedly you could get away with this in previous versions of Delphi, but this was at the expense of range checking, and typically with the inconvenience of using pointer notation. Now dynamic arrays are part of the language.

At runtime you use SetLength to specify a dynamic arrayÆs size. Once this has been done, the semantic implications of the array are pretty much the same as for a static array. One key difference, however, is that you can resize a dynamic array at will, using SetLength. Listing 3 shows two dynamic arrays in use, one of which is multi-dimensional.


Listing 3

procedure TForm1.FormCreate(Sender: TObject);
var
  S: array of String; //single dimension array
  M: array of array of String; //double dimension array
  Loop, Loop2: Byte;
begin
  //Make S 10 elements long
  SetLength(S, 10);
  ShowMessageFmt('S is %d elements long', [Length(S)]);
  for Loop := Low(S) to High(S) do
    S[Loop] := IntToStr(Loop);
  //Make M 3 by 4 elements in size
  SetLength(M, 3, 4);
  for Loop := Low(M) to High(M) do
    for Loop2 := Low(M[Loop]) to High(M[Loop]) do begin
      M[Loop, Loop2] := Format('%d, %d', [Loop, Loop2]);
      StringGrid1.Cells[Loop2, Loop] := M[Loop, Loop2]
    end;
  //Now change M, so it has 2 rows,
  //but the first row has 5 columns
  //and the second row has 10 columns
  SetLength(M, 2);
  SetLength(M[0], 5);
  SetLength(M[1], 10);
  for Loop := Low(M) to High(M) do
    for Loop2 := Low(M[Loop]) to High(M[Loop]) do begin
      M[Loop, Loop2] := Format('%d, %d', [Loop, Loop2]);
      StringGrid2.Cells[Loop2, Loop] := M[Loop, Loop2]
    end
end;

Delphi 4 supports method overloading and the overloading of global routines. This means you can declare several routines that all have the same name, but which take different parameters. Overloaded routines must be declared with the reserved word overload. When you make a call to an overloaded routine, the compiler works out which one to call by examining the parameter(s) passed in. Listing 4 shows a simple example. Figure 3 shows the Code Parameters tooltip demonstrating the various overloading method parameter possibilities.

DelphiOverload.GIF
Figure 3


Listing 4

type
  TFoo = class(TObject)
  public
    procedure Bar(C: Cardinal); overload;
    procedure Bar(D: Double); overload;
    procedure Bar(S, T: String); overload;
  end; { TFoo }
procedure ShowMe(I: Integer); overload;
procedure ShowMe(I, I2: Integer); overload;
...
procedure TForm1.FormCreate(Sender: TObject);
var Foo: TFoo;
begin
  Foo := TFoo.Create;
  Foo.Bar(40.0); //calls Double TFoo.Bar
  Foo.Bar(7); //calls Cardinal TFoo.Bar
  Foo.Bar('Hello', 'World!'); //calls String TFoo2.Bar
  Foo.Free;
  ShowMe(5); //Calls first version of ShowMe
  ShowMe(20, 10) //Calls second version of ShowMe
end;

Additionally, methods and global routines can specify default parameter values (with various restrictions outlined in the documentation). These were first seen in the command-line Delphi compiler that shipped with C++Builder 3. Figure 4 shows a routine with some default parameters, and a call about to be made to it. The Code Completion tooltip emphasises the optional nature of the default parameters using square brackets.

DelphiParams.GIF
Figure 4

After several years of being told to avoid the Real type like the plague (because it had a historic implementation that was incompatible with the maths coprocessor, and so was very inefficient), we can start to use it again. Real is now the same as Double, a 64-bit floating point number. If you really want to make use of the historic Real implementation, for backward compatibility, either use the Real48 type, or use the {$RealCompatibility On} directive.

In Delphi 2 and 3, the generic type Cardinal represented a 31-bit number (for historical reasons) giving a range of 0 to 2,147,483,647 (0 to 2G-1). Delphi 4 makes it a true 32-bit number with a range of 0 to 4,294,967,295 (0 to 4G-1, or 0 to 232-1). Additionally, there is a new fundamental type LongWord, with the same range. Delphi 4 also introduces a 64-bit fundamental type called Int64, with a massive range of -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 (-263 to 263-1, which is -8E to 8E-1). The Comp type, classed as a real type, is retained for backward compatibility only since Int64 gives better performance and is a true ordinal type.

DWord, Uint, HResult, Ole_Handle and all API handle types are now defined as LongWord instead of Integer. Var parameters of these types are therefore no longer compatible with Integer variables, so some code might need to be changed.

A new keyword has been introduced simply to remove a warning that sometimes comes up. When you inherit one class from another and declare a static method that has the same name as a virtual method in the base class, you normally get a warning telling you the new method hides the old method. Marking the new method declaration with the reintroduce keyword remedies this problem (see Listing 5).


Listing 5

TMyComponent = class(TComponent)
public
  //Since TComponent defines a virtual Loaded method, this causes a warning:
  //Method 'Loaded' hides virtual method of base type 'TComponent'
  procedure Loaded;
  //The same is true for Notification, but reintroduce removes the warning
  procedure Notification; reintroduce;
end;

When defining a class and specifying that it implements a given interface, you can now allow some other object to actually implement it. To do this, you set up a property using the new reserved word implements in the property definition. At some point, the data field underlying the property in the class gets set with an instance of an appropriate object that does implement the interface, which can be done by the delegating class itself, or by other code. This is called interface implementation by delegation (this will make COM aggregation much easier). Some example code can be found in Listing 6.


Listing 6

type
  IMyIntf = interface
    procedure Proc1;
    procedure Proc2;
  end;
  TMyImplClass = class
    procedure Proc1;
    procedure Proc2;
  end;
  TMyClass = class(TInterfacedObject, IMyIntf)
  private
    FMyImplClass: TMyImplClass;
  public
    constructor Create;
    property MyImplClass: TMyImplClass read FMyImplClass implements IMyIntf;
  end;
procedure TMyImplClass.Proc1;
...
procedure TMyImplClass.Proc2;
...
constructor TMyClass.Create;
begin
  inherited;
  FMyImplClass := TMyImplClass.Create;
end;
procedure DoIt;
var
  MyInterface: IMyIntf;
begin
  MyInterface := TMyClass.Create;
  MyInterface.Proc1; // calls TMyImplClass.Proc1;
end;

Packages now use a BPL extension, as C++Builder does. Also, these BPL files (DLLs, whose linkage is managed entirely by Delphi) now export their routines in a name-mangled fashion (just as C++ uses) rather than using a numeric suffix. C++ uses name mangling to allow the linker to distinguish between overloaded routines. Delphi now supports overloading and so the compiler does mangling such that packages work as required. I guess this now allows the Delphi and C++Builder compiler/linker to share as much code as possible. However, the BPLs created by C++Builder 3 and Delphi 4 are not shareable, as C++Builder uses VCL 3.5 and Delphi 4 uses VCL 4.

Finally, to reflect the fact that this is version 12 of InpriseÆs Object Pascal compiler, the conditional symbol VER120 is defined (Delphi 3 used VER100 and the Delphi compiler in C++Builder 3 laid claim to VER110).

VCL Enhancements

As well as support for NT services, child control flipping and the IntelliMouse wheel, which have already been mentioned, the Visual Component Library has many new capabilities.

One feature proving very popular involves the concept of an action. An action (represented by a TAction object) is a command that acts on some target object. Actions are set up in the component editor of a TActionList, a component designed to manage a list of actions (see Figure 5). Whilst you can readily make up your own actions, some sample actions are supplied in Delphi 4. These include generic cut, copy and paste actions as well as dataset navigation actions, amongst others. Once an action has been set up, you can associate it with various controls, such as menu items, buttons, bitmap buttons, speed buttons, and tool buttons, using their new Action property. The component sets its aesthetics to match those defined in the action (caption/text, glyph if applicable, hint etc.) and also reacts to the action when necessary.

DelphiAction.GIF
Figure 5

Actions provide a much more powerful way of sharing functionality around a project than simply making use of the same event handler. ItÆs worth observing that actions are considered so important that the TActionList component is the first new component on the Standard page of the component palette since Delphi 1 was released.

Actions can have an associated image that can be displayed on speed buttons, bitmap buttons, tool buttons and menu items. So menu items now support drawing an associated image, in the style of Office items. They also support full owner draw capabilities.

The drag and dock support in the IDE is only there because it was added to the VCL through changes to TControl and TWinControl(remember Delphi is written in Delphi). The DockSite property turns an arbitrary windowed control into a docking site. DragKind and DragMode are used to add drag and dock support to a control. The FloatingDockSiteClass property controls what happens when a control is turned into a floating window. A whole set of new events allows you to configure the docking and undocking actions (OnGetSiteInfo, OnDockOver, OnDockDrop, OnStartDock and OnEndDock).

Historically, you could stop a form growing or shrinking past certain sizes by writing a wm_GetMinMaxInfo message handler. Now all controls have a new property called Constraints that can govern how they resize. Additionally, the OnCanResize and OnConstrainedResize events can configure this even further. This constrained resize control in conjunction with the historic Align property and the new Anchors property can give you pretty much complete control over the geometry management of the controls on your forms. Anchors dictates how a control is anchored to its parent and so controls how it resizes when its parent resizes.

In addition to the simplistic ShortDateFormat variable that can be changed to dictate that dates are displayed and interpreted with four digits, Delphi 4 adds the more fashionable sliding window year 2000 support for those programs that insist on taking two-digit year input.

There is a new SysUtils variable called TwoDigitYearCenturyWindow that acts as a floating pivot. Years specified with only two digits are compared with the pivot by the StrToDate and StrToDateTime functions. If they are less than the current year minus TwoDigitYearCenturyWindow, they are considered to be from the next century, otherwise they are taken from the current century. A couple of examples may clarify the situation here. Table 1 shows how some different values of TwoDigitYearCenturyWindow would affect the interpretation of various dates specified with two-digit years.

Delphi 4 was released almost simultaneously with Windows 98. Consequently, Delphi now supports a whole bunch of features that are standard in Windows 98, such as running applications across multiple monitors. Forms have a DefaultMonitor property, and the Screen object has a Monitors property, an array of TMonitor objects. We also have new components that represent newer user interface controls such as a control bar (a bit like an easy to set up coolbar), flat scrollbars on listviews, a page scroller (like a scroll box, but with arrows instead of scrollbars) and a month calendar (like the drop down part of a TDateTimePicker, but as a control of its own). Most of this support works fine in Windows 95 with the appropriate version of ComCtl32.Dll installed. The Delphi 4 installation installs the Internet Explorer 4.01 version of this library, and it is important to remember this fact when deploying your applications to Windows 95 users. You can always get the most up to date version of this library from www.microsoft.com/msdn/downloads/files/40comupd.htm


Table 1

Current year

TwoDigitYearCenturyWindow

Pivot

1/1/03

1/1/50

1/1/70

1/1/99

1998

0 (default)

1998

1/1/1903

1/1/1950

1/1/1970

1/1/1999

2001

0 (default)

2001

1/1/2003

1/1/2050

1/1/2070

1/1/2099

1998

10

1988

1/1/2003

1/1/2050

1/1/2070

1/1/1999

2001

10

1991

1/1/2003

1/1/2050

1/1/2070

1/1/1999

1998

50

1948

1/1/2003

1/1/1950

1/1/1970

1/1/1999

2001

50

1951

1/1/2003

1/1/2050

1/1/1970

1/1/1999

Middle-east locale support includes the FlipChildren method mentioned previously, as well as the BiDiMode and ParentBiDiMode properties, which in appropriate versions of Windows affect the direction that text reads from and where vertical scrollbars are placed.

TObject has new virtual protected methods, AfterConstruction and BeforeDesctruction. Code that shouldnÆt execute in the constructor or destructor can go in here. TCustomForm triggers its OnCreate and OnDestroy events from these. These methods were primarily added for components that will be used in C++Builder, due to differences in constructor and destructor behaviour between it and Delphi.

For those who like sniffing around inside Delphi, the Open Tools API has been extended. All the old classes are still there, but it looks like they may now be superceded by the interfaces defined in the ToolsApi.Pas file. For example, the old RegisterLibraryExpert routine for telling Delphi about a DLL-based wizard seems to be replaced by RegisterPackageWizard, for wizards that reside in a package. These new interfaces provide much enhanced access to the IDEÆs workings for third-party add-in tool developers. Notable additions to the Open Tools API include facilities for hooking into the debugger and customising breakpoint behaviour (roll on third party profilers).

Debugging Support

The debugging facilities have been extended to surpass those available in C++Builder 3. Delphi now supports debugging multiple processes, and also includes support for remote debugging. This means that you can, say, debug your middle-tier server applications from a client machine.

Delphi also supports attaching to arbitrary processes (albeit through undocumented mechanisms). Adding a new string value called Enable Attach Menu to Delphi 4Æs Debugging section in the Windows registry with a value of -1 makes the Run | Attach to Process... menu item visible, which invokes a dialog full of process ids (PIDs) that you can attach the debugger to. Similar functionality is available through the /attach:<PID> command line parameter, which with a bit of Windows know-how can be used to turn Delphi 4 into a JIT (just in time) Win32 post-mortem debugger (see Issue 3Æs C++Builder 3 review or a Windows guru for details).

There is another new dialog (accessed through Tools | Debugger Options...) dedicated to the debugger. This is where you tell the debugger whether it should pop up or not for program exceptions, except that now you can completely customise which exceptions the debugger will ignore, and which it will break for.

The various debugger windows accessible from the View menu have now been tidied away into their own submenu: View | Debug Windows (see Figure 6). In addition to the old debug windows that could be launched (breakpoints, call stack, watches, threads and modules) we can now see all the local variables (and their values) defined in the current routine.

DelphiDebug.GIF
Figure 6

Also, we now have an event log to play with. The log can list Windows messages, process/module loads and unloads, debug messages (marked with ODS, for OutputDebugString) as well as breakpoints, exceptions and custom comments. Figure 1 demonstrates the fact that they (as well as most of the other IDE windows) can be docked in a tiled or tabbed manner.

The module view in Delphi 3 provided a handy way to identify which packages are used by an application, to help with deployment logistics. Now it has been enhanced with extra panes that list the available source files from the selected module and the exported routines from that module.

The previously undocumented (and not particularly fully implemented) CPU window is now available from the IDE without any cunning registry tricks. For debugging at the machine level, including assembler instruction viewing and CPU register analysis, we now have a viable alternative to Turbo Debugger 32. You can now also opt to use Turbo Debugger keystrokes whilst debugging.

In Delphi 3 breakpoints were reasonably flexible, triggering either every time, every fixed number of times, or when a condition was true. However, they were always based around a source code line being executed. Delphi 4 allows you to set a breakpoint based around an instruction executing at a specified memory location, or a specified memory location being written to, or even when a specified module is loaded.

The last prime additional debugging feature is the introduction of data inspectors. An inspector is a bit like a watch on overdrive (see Figure 7). Opening an inspector on some object will display all its data fields, methods and properties, allowing you to change values where appropriate. You can also drill down, launching other inspectors based upon an item found in an inspector.

DelphiInspect.GIF
Figure 7

ActiveX And COM

On the ActiveX page of the File | New... dialog, you can still create Automation objects, but now with optional support for events through additional connection point interfaces. Also, you can create a smaller COM object that does not support Automation (with or without a type library).

The COM support is now thread-safe, and supports four possibilities: single (no thread support), apartment (STA or single threaded apartment), free (MTA or multi threaded apartment) and both (apartment and free).

The business of writing data-aware ActiveX controls is now supported, as is the use of third party data-bound ActiveX controls, which can now talk to VCL datasets.

The type library editor has been extended and allows you to view the various type library entities in either IDL or Object Pascal (the default). You can also export the type library to either Microsoft IDL or to CORBA IDL.

MTS

Microsoft Transaction Server (MTS) is a runtime environment, hosted on a server machine, that provides transaction services, security, resource pooling and middle-tier deployment management for distributed COM applications; it is a distributed application server. In a way, it is MicrosoftÆs way of making COM more CORBA-like, as the CORBA standard already defines services for transactions, security, naming etc.

Delphi 4 Client/Server Suite provides complete support for writing objects that can be installed into the MTS runtime environment (or MTS executive). To start with, in File| New...|Multitier, you can choose wizards to make an MTS Object or an MTS Data Module. Both these options need an ActiveX Library project to be active, and will happily make one for you (with a .DLL extension) if needed. An MTS Object is a dual interface Automation object that implements the relevant IObjectControl interface, and gains access to an object that implements IObjectContext. These are the two key interfaces with regard to an MTS object.

An MTS data module (TMtsDataModule) is the same as an MTS object, but is inherited from TRemoteDataModule instead of TMtsAutoObject. It still implements IObjectControl and gains access to an IObjectContext to fulfil the MTS requirements. You can use an MTS data module as part of the middle tier of a multitier MIDAS system, which would consist of an MTS package of objects set up to run in an instance of the MTS executive.

Just as with automation objects and COM objects with type library support, the development of the skeletal structure of your MTS objects is done using the type library editor. An extra page is available when your coclass is selected in the type library editor that allows you to specify your exact transaction requirements, in case you changed your mind since creating the object in the first place. When you have added all the required entities, using the tool buttons and context menus, you can press the Refresh button to generate the necessary interface and enumeration declarations (amongst possible other types) as well as the coclass implementations.

When you have finished writing your MTS objects, you install them into new or existing MTS packages using Run | Install MTS Objects....

For resource pooling, MTS has the concept of resource dispensers. BDE 5 has now been implemented as an MTS resource dispenser. This means that any BDE database connection can be managed and pooled by MTS. So any databases can be accessed from within MTS objects in a controlled and managed manner, thanks to the new BDE.

MTS does not support object pooling in its current version, but the stubs are in place. Delphi already supports object pooling by making use of these do-nothing stubs. When MTS object pooling arrives, Delphi 4 will be ready.

CORBA

DelphiÆs MTS support is provided to enhance the COM support, so developers can write COM objects that run within MTS and be managed and maintained by it. Delphi also offers support for CORBA objects. CORBA (Common Object Request Broker Architecture) is the standard developed by the Object Management Group for developing distributed object applications.

CORBA is very much like COM, in that CORBA client applications can access CORBA objects implemented on remote servers. However, CORBA is not Windows-specific, and so a CORBA client application can communicate with objects on non-Windows platforms. Also, CORBA predefines many services that are not part of standard COM (although some of which are effectively available to COM objects running within MTS) such as security, transaction control, object naming, life cycle management and persistence.

Delphi 4 Client/Server Suite offers the same easy to use, high-level support for CORBA as can be found for MTS objects or normal COM objects. On the File | New... | Multitier dialog page you can find wizards for making a CORBA object or a CORBA data module. These are very similar, in principle, to the high level MTS object and MTS data module (albeit with differing implementations). The CORBA object represents an object that can reside in some CORBA server application and be accessed by a CORBA client. The CORBA data module represents the middle tier in a CORBA-based MIDAS application.

Again, the type library editor is used to build up your COM objects. When the Refresh button is pushed, all the relevant types get declared, and a stub and skeleton class is implemented for each defined CORBA class.

For CORBA to work, an ORB (Object Request Broker) must be available to allow objects to talk to each other. Delphi comes equipped with Inprise VisiBroker as the basis of its CORBA support.

ItÆs worth mentioning at this point that whilst DelphiÆs help has been criticised in the past, all this new technology is documented extensively with lots of help pages chained together into browse sequences.

MIDAS Support

The old MIDAS components TMIDASConnection (from Delphi Client/Server Suite 3.01) and TRemoteServer (from version 3.00) have been superseded by a set of four components: TDCOMConnection (identical to TRemoteServer, which has moved from the DBClient unit to MidasCon), TOLEnterpriseConnection, TSocketConnection and TCorbaConnection. Connections to middle-tier servers made using the DCOM and socket components have a new ObjectBroker property that allows client side brokering. OLEnterprise provides its own brokering via the Business Object Broker and a CORBA connection also offers brokering services through its ORB.

All these connection components (except the CORBA one) add support for customised login to the application server. Also, the socket connection supports installing a connection message interceptor, for example to support encryption and decryption.

The client dataset component has new facilities for easily passing parameter values back to some dataset on the application server. It also supports maintained aggregates, object-relational field types and gives better support for programming applications that adhere to the æbriefcase modelÆ. Record set filtering is much improved by a more extensive filter syntax and also the facility for the client to send parameters to the server, which the server takes and uses to reduce the record set sent back.

Other improvements in this area involve master/detail support using nested tables, added support for saving custom information in data packets and more control over how updates are performed.

When you develop a MIDAS middle-tier application, whether you choose to use COM, MTS, OLEnterprise or CORBA, the main development path is the same. You choose an appropriate form of data module, drop on the dataset components of choice, and make them available to client applications through a provider object, and implement the data manipulation business logic. Depending upon the type of server you develop, the client will connect to it using an appropriate TCustomRemoteServer descendent component.

Database Enhancements

The TTable component has new component editors that allow you to alter your underlying data storage model from within Delphi, or allow Delphi to keep up with external changes. Having set the TableName property, you can rename or delete the physical table, or get the TTable to re-sync itself with a possibly altered physical table structure. You can choose a non-existent TableName, and use the new property editors for FieldDefs and IndexDefs, and then create the physical table directly from the form designer.

Delphi 3 shipped with BDE 4 (3.01 shipped with 4.01), which could talk to Access tables through MS DAO 3.0 (Access 95). BDE 4.51 (supplied with C++Builder 3 and dBASE 7 for Windows) added support for DAO 3.5 (Access 97). BDE 5, as shipped with Delphi 4, gives Delphi users Access 97 support, but also adds new field component types to support the Oracle 8 extended field types, including abstract data types (ADTs), arrays, references and nested tables. The TDBGrid also supports viewing ADTs and nested tables.

TQuery components can now have their SQL defined by the Inprise-written SQL Builder, which is claimed to be an intelligent replacement for the Visual Query Builder. One example of the difference between the two query builders is that the new version will use the SQL JOIN syntax where appropriate.

Product Versions

As usual, this product is supplied in three mainstream versions: Desktop, Professional and Client/Server Suite. An Enterprise version will arrive later in the year with additional Enterprise-wide support built-in, such as for Inprise Entera 4. Some things specific to the Client/Server Suite include CORBA and MTS support, service applications, remote debugging, the web application wizard, SQL Builder, Oracle 8 extended support, Inprise MIDAS, MTS and CORBA support.

Conclusion

The initial reaction to the Delphi 4 press release from many lone developers and those working in small companies was slightly negative. Their impression was that there isnÆt enough new stuff of substance in the product. Sure, there are new tons of new features, but if youÆre not into the whole CORBA, COM and MTS scene, then most of it is IDE enhancements. You can get a lot of the same functionality with add-in tools, such as CodeRush and third party class browsers.

You can judge for yourselves after reading this review. I rather like it all. Certainly, if you are interested in writing distributed applications, then you wonÆt be disappointed with the consistent way in which you develop applications for the various available architectures. But even if you were to skip the Client/Server Suite and opt for the Professional version, IÆm confident there is lots in the product to keep you happy until the next release.

If you want more information on some of the various new features of Delphi 4, you could do worse than visit Bob SwartÆs Delphi 4 page at www.drbob42.com/delphi4, and of course get a subscription to The Delphi Magazine (www.itecuk.com)!

Brian Long is an independent consultant and trainer. You can reach him by email as brian@blong.com

Second Opinion: Keeping An Eye on the Ball..

Dave Jewell, Technical Editor

Fond as I am of Delphi, I have some real concerns about the direction in which Inprise is headed. Historically, Borland (before the name change!) had a reputation for building lean, fast development systems which were far simpler to use than MicrosoftÆs offerings. The company now seems to be sacrificing those advantages on the altar of the great god æEnterpriseÆ.

Delphi 4 is a good deal less lean than itÆs predecessors and, if youÆre not a regular worshipper of the aforementioned god, itÆs hard to see what benefits are to be gained from the upgrade. OK, there are some great language enhancements (dynamically resizable arrays being in the must-have category for number-crunching gurus) but the IDE enhancements are a mere shadow of what CodeRush has to offer.

If you donÆt mind programming in write-only Sanskrit, Visual C++ is great for creating tiny ActiveX controls. But Visual J++ 6.0 will now produce small, tight OCX controls that require only the presence of the Microsoft Java VM. By contrast, Delphi-authored OCXs are significantly larger and much less easy to author than is the case with VJ++ 6.0 (if youÆre not converting an existing VCL control, then youÆve got a lot of work on your hands). Worse, Delphi 4 is just as flaky as Delphi 3 (more so, in my experience) when it comes to importing OCX controls into a Delphi project. Several times, IÆve imported OCXs into Delphi 3 which generate all sorts of compiler errors in version 4. This doesnÆt bode well in an increasingly COM-centric world.

What would I like to have seen in Delphi 4? How about a source code formatter? How about a comprehensive control wizard (VCL and ActiveX) along the lines of the CodeRush CDK? How about a raft of new VCL controls that give developers the same look and feel as Office97? Dockable toolbars have been with us for a while (eg Jordan RussellÆs excellent ToolBar97) but IÆd also like to see a component for creating animated æassistantsÆ, another for creating OutLook-style control bars and so on. These controls are already starting to appear from other smaller developers, but what a pity they donÆt come with Delphi itself.

We all recognise that Inprise have been through tough times over the past few years, and that their development resources arenÆt what they used to be. Fair enough, but in my view, this is all the more reason not to put too many eggs into the Enterprise basket. At the end of the day, Delphi is well-known and loved by many small developers, with relatively poor penetration amongst large corporates. A new company name wonÆt change that overnight, so donÆt be too quick to bite the hand that feeds you...